﻿Imports System.ComponentModel

Public Class SortableListView
    Inherits ListView

    Private LastSortedOnColumn As SortableGridViewColumn
    Private LastDirection As ListSortDirection = ListSortDirection.Ascending
    Private LastSortBy As String

#Region "New Dependency Properties"


    Public Property ColumnHeaderSortedAscendingTemplate() As String
        Get
            Return CStr(GetValue(ColumnHeaderSortedAscendingTemplateProperty))
        End Get
        Set(ByVal value As String)
            SetValue(ColumnHeaderSortedAscendingTemplateProperty, value)
        End Set
    End Property

    ' Using a DependencyProperty as the backing store for ColumnHeaderSortedAscendingTemplate.  This enables animation, styling, binding, etc...
    Public Shared ReadOnly ColumnHeaderSortedAscendingTemplateProperty As DependencyProperty = _
        DependencyProperty.Register("ColumnHeaderSortedAscendingTemplate", GetType(String), GetType(SortableListView), New UIPropertyMetadata("ColumnHeaderSortedAscendingTemplate"))


    Public Property ColumnHeaderSortedDescendingTemplate() As String
        Get
            Return CStr(GetValue(ColumnHeaderSortedDescendingTemplateProperty))
        End Get
        Set(ByVal value As String)
            SetValue(ColumnHeaderSortedDescendingTemplateProperty, value)
        End Set
    End Property

    ' Using a DependencyProperty as the backing store for ColumnHeaderSortedDescendingTemplate.  This enables animation, styling, binding, etc...
    Public Shared ReadOnly ColumnHeaderSortedDescendingTemplateProperty As DependencyProperty = _
            DependencyProperty.Register("ColumnHeaderSortedDescendingTemplate", GetType(String), GetType(SortableListView), New UIPropertyMetadata("ColumnHeaderSortedDescendingTemplate"))


    Public Property ColumnHeaderNotSortedTemplate() As String
        Get
            Return CStr(GetValue(ColumnHeaderNotSortedTemplateProperty))
        End Get
        Set(ByVal value As String)
            SetValue(ColumnHeaderNotSortedTemplateProperty, value)
        End Set
    End Property

    ' Using a DependencyProperty as the backing store for ColumnHeaderNotSortedTemplate.  This enables animation, styling, binding, etc...
    Public Shared ReadOnly ColumnHeaderNotSortedTemplateProperty As DependencyProperty = _
            DependencyProperty.Register("ColumnHeaderNotSortedTemplate", GetType(String), GetType(SortableListView), New UIPropertyMetadata(""))
#End Region

    '
    ' Executes when the control is initialized completely the first time through. Runs only once.
    '
    '
    Protected Overrides Sub OnInitialized(ByVal e As EventArgs)

        ' add the event handler to the GridViewColumnHeader. This strongly ties this ListView to a GridView.
        Me.AddHandler(GridViewColumnHeader.ClickEvent, New RoutedEventHandler(AddressOf GridViewColumnHeaderClickedHandler))

        ' cast the ListView's View to a GridView
        Dim gridView = DirectCast(Me.View, GridView)
        If gridView IsNot Nothing Then
            ' determine which column is marked as IsDefaultSortColumn. Stops on the first column marked this way.

            Dim sortableGridViewColumn As SortableGridViewColumn = Nothing

            For Each gridViewColumn In gridView.Columns

                If Not TypeOf gridViewColumn Is SortableGridViewColumn Then
                    Continue For
                End If

                sortableGridViewColumn = DirectCast(gridViewColumn, SortableGridViewColumn)
                If sortableGridViewColumn IsNot Nothing Then
                    If sortableGridViewColumn.IsDefaultSortColumn Then
                        Exit For
                    End If
                    sortableGridViewColumn = Nothing
                End If
            Next

            ' if the default sort column is defined, sort the data and then update the templates as necessary.
            If sortableGridViewColumn IsNot Nothing Then
                LastSortedOnColumn = sortableGridViewColumn

                Sort(sortableGridViewColumn.SortPropertyName, sortableGridViewColumn.DefaultSortDirection)

                If sortableGridViewColumn.DefaultSortDirection = ListSortDirection.Ascending AndAlso _
                   Not String.IsNullOrEmpty(Me.ColumnHeaderSortedAscendingTemplate) Then
                    sortableGridViewColumn.HeaderTemplate = DirectCast(Me.TryFindResource(ColumnHeaderSortedAscendingTemplate), DataTemplate)

                ElseIf sortableGridViewColumn.DefaultSortDirection = ListSortDirection.Descending AndAlso _
                    Not String.IsNullOrEmpty(Me.ColumnHeaderSortedDescendingTemplate) Then
                    sortableGridViewColumn.HeaderTemplate = DirectCast(Me.TryFindResource(ColumnHeaderSortedDescendingTemplate), DataTemplate)

                End If
                Me.SelectedIndex = 0
            End If
        End If
        MyBase.OnInitialized(e)
    End Sub

    '
    ' Event Handler for the ColumnHeader Click Event.
    '
    '
    '
    Private Sub GridViewColumnHeaderClickedHandler(ByVal sender As Object, ByVal e As RoutedEventArgs)
        If Not TypeOf e.OriginalSource Is GridViewColumnHeader Then
            Exit Sub
        End If

        Dim headerClicked = DirectCast(e.OriginalSource, GridViewColumnHeader)

        ' ensure that we clicked on the column header and not the padding that's added to fill the space.
        If headerClicked IsNot Nothing AndAlso headerClicked.Role <> GridViewColumnHeaderRole.Padding Then


            If Not TypeOf headerClicked.Column Is SortableGridViewColumn Then
                Exit Sub
            End If

            ' attempt to cast to the sortableGridViewColumn object.
            Dim sortableGridViewColumn = DirectCast(headerClicked.Column, SortableGridViewColumn)

            ' ensure that the column header is the correct type and a sort property has been set.
            If sortableGridViewColumn IsNot Nothing AndAlso Not String.IsNullOrEmpty(sortableGridViewColumn.SortPropertyName) Then

                Dim direction As ListSortDirection
                Dim newSortColumn As Boolean


                ' determine if this is a new sort, or a switch in sort direction.
                If LastSortedOnColumn Is Nothing _
                    OrElse String.IsNullOrEmpty(LastSortedOnColumn.SortPropertyName) _
                    OrElse Not String.Equals(sortableGridViewColumn.SortPropertyName, LastSortedOnColumn.SortPropertyName, StringComparison.InvariantCultureIgnoreCase) Then

                    newSortColumn = True
                    direction = ListSortDirection.Ascending
                Else
                    If LastDirection = ListSortDirection.Ascending Then
                        direction = ListSortDirection.Descending
                    Else
                        direction = ListSortDirection.Ascending
                    End If
                End If

                ' get the sort property name from the column's information.
                Dim sortPropertyName = sortableGridViewColumn.SortPropertyName

                ' Sort the data.
                Sort(sortPropertyName, direction)

                If direction = ListSortDirection.Ascending Then
                    If Not String.IsNullOrEmpty(Me.ColumnHeaderSortedAscendingTemplate) Then
                        sortableGridViewColumn.HeaderTemplate = DirectCast(Me.TryFindResource(ColumnHeaderSortedAscendingTemplate), DataTemplate)
                    Else
                        sortableGridViewColumn.HeaderTemplate = Nothing
                    End If
                Else
                    If Not String.IsNullOrEmpty(Me.ColumnHeaderSortedDescendingTemplate) Then
                        sortableGridViewColumn.HeaderTemplate = DirectCast(Me.TryFindResource(ColumnHeaderSortedDescendingTemplate), DataTemplate)
                    Else
                        sortableGridViewColumn.HeaderTemplate = Nothing
                    End If
                End If

                ' Remove arrow from previously sorted header
                If newSortColumn AndAlso LastSortedOnColumn IsNot Nothing Then

                    If Not String.IsNullOrEmpty(Me.ColumnHeaderNotSortedTemplate) Then
                        LastSortedOnColumn.HeaderTemplate = DirectCast(Me.TryFindResource(ColumnHeaderNotSortedTemplate), DataTemplate)
                    Else
                        LastSortedOnColumn.HeaderTemplate = Nothing
                    End If
                End If
                LastSortedOnColumn = sortableGridViewColumn
            End If
        End If
    End Sub

    '
    ' Helper method that sorts the data.
    '
    '
    '
    Private Sub Sort(ByVal sortBy As String, ByVal direction As ListSortDirection)

        If Me.ItemsSource Is Nothing Then
            Exit Sub
        End If

        LastSortBy = sortBy
        LastDirection = direction
        Dim dataView = CollectionViewSource.GetDefaultView(Me.ItemsSource)

        dataView.SortDescriptions.Clear()
        Dim sd = New SortDescription(sortBy, direction)
        dataView.SortDescriptions.Add(sd)
        dataView.Refresh()
    End Sub

    Public Sub Resort()
        If LastSortBy IsNot Nothing Then
            Sort(LastSortBy, LastDirection)
        End If
    End Sub

End Class
